﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;

namespace DaliUsb
{
  internal static class SetupApi
  {
    private const int DIGCF_DEFAULT = 0x1;
    private const int DIGCF_PRESENT = 0x2;
    private const int DIGCF_ALLCLASSES = 0x4;
    private const int DIGCF_PROFILE = 0x8;
    private const int DIGCF_DEVICEINTERFACE = 0x10;


    internal static IEnumerable<string> FindDevicePath(Guid classGuid, int vid, int pid)
    {
      uint i;
      IntPtr infoSet;
      SP_DEVINFO_DATA deviceData;
      SP_DEVICE_INTERFACE_DATA interfaceData;
      SP_DEVICE_INTERFACE_DETAIL_DATA detailData;
      uint requiredSize;
      string find;
      List<string> devices;

      devices = new List<string>();
      find = string.Format("vid_{0:x4}&pid_{1:x4}", vid, pid);
      infoSet = SetupDiGetClassDevs(ref classGuid, null, IntPtr.Zero, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT);	// this gets a list of all HID devices currently connected to the computer (InfoSet)

      try
      {
        deviceData = new SP_DEVINFO_DATA();
        deviceData.cbSize = (uint)Marshal.SizeOf(deviceData);

        interfaceData = new SP_DEVICE_INTERFACE_DATA();
        interfaceData.cbSize = (uint)Marshal.SizeOf(interfaceData);

        detailData = new SP_DEVICE_INTERFACE_DETAIL_DATA();

        // for 64 bit operating systems
        if (IntPtr.Size == 8)
        {
          detailData.cbSize = 8;
        }
        else
        {
          detailData.cbSize = 4 + (uint)Marshal.SystemDefaultCharSize; // for 32 bit systems
        }


        i = 0;
        while (SetupDiEnumDeviceInterfaces(infoSet, 0, ref classGuid, i, ref interfaceData))	// this gets the device interface information for a device at index 'nIndex' in the memory block
        {
          requiredSize = 0;

          if (!SetupDiGetDeviceInterfaceDetail(infoSet, ref interfaceData, IntPtr.Zero, 0, ref requiredSize, IntPtr.Zero))
          {
            if (SetupDiGetDeviceInterfaceDetail(infoSet, ref interfaceData, ref detailData, requiredSize, ref requiredSize, IntPtr.Zero))
            {
              if (detailData.DevicePath.IndexOf(find) >= 0)
              {
                devices.Add(detailData.DevicePath);
              }
            }
          }

          i++;
        }

        return devices;
      }
      finally
      {
        SetupDiDestroyDeviceInfoList(infoSet);
      }
    }

    /// <summary>
    /// Allocates an InfoSet memory block within Windows that contains details of devices.
    /// </summary>
    /// <param name="ClassGuid">Class guid (e.g. HID guid)</param>
    /// <param name="Enumerator">Not used</param>
    /// <param name="hwndParent">Not used</param>
    /// <param name="Flags">Type of device details required (DIGCF_ constants)</param>
    /// <returns>A reference to the InfoSet</returns>
    [DllImport("setupapi.dll", CharSet = CharSet.Auto)]
    private static extern IntPtr SetupDiGetClassDevs(ref Guid ClassGuid, [MarshalAs(UnmanagedType.LPTStr)] string Enumerator, IntPtr hwndParent, UInt32 Flags);


    [DllImport("setupapi.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern Boolean SetupDiEnumDeviceInterfaces(IntPtr hDevInfo, uint devInfo, ref Guid interfaceClassGuid, UInt32 memberIndex, ref SP_DEVICE_INTERFACE_DATA deviceInterfaceData);

    [DllImport("setupapi.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern bool SetupDiDestroyDeviceInfoList(IntPtr hDevInfo);

    [DllImport("setupapi.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern Boolean SetupDiGetDeviceInterfaceDetail(IntPtr hDevInfo, ref SP_DEVICE_INTERFACE_DATA deviceInterfaceData, ref SP_DEVICE_INTERFACE_DETAIL_DATA deviceInterfaceDetailData, UInt32 deviceInterfaceDetailDataSize, ref UInt32 requiredSize, IntPtr lpDeviceInfoDat);

    [DllImport("setupapi.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern Boolean SetupDiGetDeviceInterfaceDetail(IntPtr hDevInfo, ref SP_DEVICE_INTERFACE_DATA deviceInterfaceData, IntPtr lpDeviceInterfaceDetailData, UInt32 deviceInterfaceDetailDataSize, ref UInt32 requiredSize, IntPtr lpDeviceInfoDat);


  }
}
